﻿#region LGPL License

/*
OpenSearchNet
Copyright (c) 2014, Brad Hards <bradh@frogmouth.net>, All rights reserved.

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3.0 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library.
*/

#endregion LGPL License

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace OpenSearch
{
    /// <summary>
    /// One item within a query Response
    /// </summary>
    public class ResponseItem
    {
        private double relevance = 1.0;

        private string title;
        private string id;
        private DateTime published = new DateTime();
        private DateTime lastUpdated = new DateTime();
        private Collection<Link> links;
        private ResponseItemContent content;
        private string summary;
        private System.Collections.ObjectModel.Collection<System.ServiceModel.Syndication.SyndicationPerson> authors = new System.Collections.ObjectModel.Collection<System.ServiceModel.Syndication.SyndicationPerson>();
        private System.Collections.ObjectModel.Collection<System.ServiceModel.Syndication.SyndicationPerson> contributors = new System.Collections.ObjectModel.Collection<System.ServiceModel.Syndication.SyndicationPerson>();

        private string federationResultSourceId;

        /// <summary>
        /// Build a ResponseItem from a raw SyndicationItem (Atom or RSS fragment)
        /// </summary>
        /// <param name="item">SyndicationItem to parse into this ResponseItem</param>
        internal ResponseItem(System.ServiceModel.Syndication.SyndicationItem item)
        {
            try
            {
                relevance = item.ElementExtensions.ReadElementExtensions<double>("score", Constants.NSOpenSearchRelevance)[0];
            }
            catch (NullReferenceException)
            {
                relevance = 1.0;
            }
            catch (ArgumentOutOfRangeException)
            {
                relevance = 1.0;
            }
            try
            {
                title = System.Net.WebUtility.HtmlDecode(item.Title.Text);
            }
            catch (NullReferenceException)
            {
                title = "";
            }
            try
            {
                published = item.PublishDate.DateTime;
            }
            catch (NullReferenceException)
            {
                published = new DateTime();
            }
            try
            {
                lastUpdated = item.LastUpdatedTime.DateTime;
            }
            catch (NullReferenceException)
            {
                lastUpdated = new DateTime();
            }
            try
            {
                id = item.Id;
            }
            catch (NullReferenceException)
            {
                id = "";
            }
            try
            {
                System.Xml.XmlElement resultSourceElement = item.ElementExtensions.ReadElementExtensions<System.Xml.XmlElement>("resultSource", Constants.NSOpenSearchFederation)[0];
                federationResultSourceId = resultSourceElement.GetAttribute("sourceId", Constants.NSOpenSearchFederation);
            }
            catch (NullReferenceException)
            {
                federationResultSourceId = "";
            }
            catch (ArgumentOutOfRangeException)
            {
                federationResultSourceId = "";
            }
            try
            {
                links = new Collection<Link>();
                foreach (System.ServiceModel.Syndication.SyndicationLink rawLink in item.Links)
                {
                    links.Add(new Link(rawLink));
                }
            }
            catch (NullReferenceException)
            {
                links = new Collection<Link>();
            }
            try
            {
                content = new ResponseItemContent(item.Content);
            }
            catch (NullReferenceException)
            {
                content = new ResponseItemContent();
            }
            try
            {
                summary = item.Summary.Text;
            }
            catch (NullReferenceException)
            {
                summary = "";
            }
            try
            {
                authors = item.Authors;
            }
            catch (NullReferenceException)
            {
                authors = new System.Collections.ObjectModel.Collection<System.ServiceModel.Syndication.SyndicationPerson>();
            }
            try
            {
                contributors = item.Contributors;
            }
            catch (NullReferenceException)
            {
                authors = new System.Collections.ObjectModel.Collection<System.ServiceModel.Syndication.SyndicationPerson>();
            }
        }

        /// <summary>
        /// Relevance of this item to the search request terms
        /// </summary>
        /// <remarks>
        /// <para>
        /// Relevance is an extension to the OpenSearch specification that allows a search server to indicate a relative score
        /// in a particular domain for an individual search result. The relevance value is a number from 0.0 to 1.0 inclusive.
        /// </para>
        /// <para>
        /// The server does not need to provide this information.
        /// </para>
        /// </remarks>
        public double Relevance
        {
            get { return relevance; }
        }

        /// <summary>
        /// Title of this item
        /// </summary>
        /// <remarks>
        /// <para>
        /// This is typically (but not always) a short human readable title associated with the search response item.
        /// </para>
        /// <para>
        /// If the server does not provide a title, this property will return an empty string.
        /// </para>
        /// </remarks>
        public string Title
        {
            get { return title; }
        }

        /// <summary>
        /// When the item was published
        /// </summary>
        /// <remarks>
        /// <para>
        /// If the server does not provide this information, a default (invalid) date and time will be returned.
        /// </para>
        /// </remarks>
        public DateTime Published
        {
            get { return published; }
        }

        /// <summary>
        /// When the item was last updated
        /// </summary>
        /// <remarks>
        /// <para>
        /// If the server does not provide this information, a default (invalid) date and time will be returned.
        /// </para>
        /// </remarks>
        public DateTime LastUpdated
        {
            get { return lastUpdated; }
        }

        /// <summary>
        /// Federated source identifier
        /// </summary>
        /// <remarks>
        /// <para>
        /// For cases where the search server is actually proxing for one or more back-end services, this
        /// property identifies the back-end service that provided the results.
        /// </para>
        /// <para>
        /// This property corresponds to the <c>sourceId</c> attribute in a <c>resultSource</c> element, as
        /// described in IC/DoD Interface Encoding Specification for Brokered Search V1.1, 12 May 2011.
        /// </para>
        /// </remarks>
        public string FederationResultSourceId
        {
            get { return federationResultSourceId; }
        }

        /// <summary>
        /// Search response item identifier
        /// </summary>
        /// <remarks>
        /// <para>
        /// This property corresponds to the <c>id</c> attribute in an Atom feed. It is not usually suitable for display
        /// to the user.
        /// </para>
        /// <para>
        /// If the server does not provide this information, an empty string will be returned.
        /// </para>
        /// </remarks>
        public string Id
        {
            get { return id; }
        }

        /// <summary>
        /// Links that are associated with this search item
        /// </summary>
        public ICollection<Link> Links
        {
            get { return links; }
        }

        /// <summary>
        /// Content of the search item
        /// </summary>
        public ResponseItemContent Content
        {
            get { return content; }
        }

        /// <summary>
        /// Summary of the search item
        /// </summary>
        public string Summary
        {
            get { return summary; }
        }

        /// <summary>
        /// Authors for the search response item
        /// </summary>
        /// <remarks>
        /// <para>
        /// This usually corresponds to the <c>Author</c> element(s) in an OpenSearch response, which is part of the Atom namespace. The server
        /// does not need to provide this information. If it is not provided, an empty list will be returned.
        /// </para>
        /// <para>
        /// There is also an Author at the Response level.
        /// </para>
        /// </remarks>
        public System.Collections.ObjectModel.Collection<System.ServiceModel.Syndication.SyndicationPerson> Authors
        {
            get { return authors; }
        }

        /// <summary>
        /// Contributors for the search response item
        /// </summary>
        /// <remarks>
        /// <para>
        /// This usually corresponds to the <c>Contributor</c> element(s) in an OpenSearch response, which is part of the Atom namespace. The server
        /// does not need to provide this information. If it is not provided, an empty list will be returned.
        /// </para>
        /// </remarks>
        public System.Collections.ObjectModel.Collection<System.ServiceModel.Syndication.SyndicationPerson> Contributors
        {
            get { return contributors; }
        }
    }
}